package BLC

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math/big"
)

//0000 0000 0000 0000 0101 1100 0100 ....0001
//256位hash的前面最少要有16个0
const targetBit = 20

type ProofOfWork struct {
	Block  *Block   //当前要验证的区块
	target *big.Int //大数据存储，解决数据溢出的问题。代表难度
}

//对区块的数据进行拼接，返回字节数组
func (pow *ProofOfWork) prepareData(nonce int) []byte {
	data := bytes.Join(
		[][]byte{
			pow.Block.PrevBlockHash,           //上一个区块的hash
			pow.Block.Data,                    //当前区块的交易数据
			IntToHex(pow.Block.Timestamp),     //时间戳
			IntToHex(int64(targetBit)),        //挖矿难度（可有可无）
			IntToHex(int64(nonce)),            //用于工作量证明的nonce值
			IntToHex(int64(pow.Block.Height)), //区块高度
		},
		[]byte{},
	)
	return data
}

//工作量证明执行代码，进行挖矿
func (proofOfWork *ProofOfWork) Run() ([]byte, int64) {
	//1.将Block的属性拼接成字节数组
	//2.生成hash
	//3.判断hash的有效性，如果满足条件就跳出循环
	nonce := 0
	var hashInt big.Int //存储新生成的hash值（将hash转化成int型）
	var hash [32]byte   //全局hash

	for {
		//准备数据
		dataBytes := proofOfWork.prepareData(nonce)
		//对拼接后的区块数据生成hash
		hash = sha256.Sum256(dataBytes)
		fmt.Printf("\r%x", hash)
		//将hash存储在hashInt
		hashInt.SetBytes(hash[:])
		//判断hashInt是否小于Block里面的Target
		// Cmp compares x and y and returns:
		//   -1 if x <  y
		//    0 if x == y
		//   +1 if x >  y
		if proofOfWork.target.Cmp(&hashInt) == 1 {
			break
		}
		nonce = nonce + 1
	}

	return hash[:], int64(nonce)
}

//创建新的工作量证明对象
func NewProofOfWork(block *Block) *ProofOfWork {
	//1.创建一个初始值为1的target
	target := big.NewInt(1)
	//2.左移256-targetBit
	target = target.Lsh(target, 256-targetBit)

	return &ProofOfWork{Block: block, target: target}
}
